/*
 * Copyright (c) 2002-2006 by OpenSymphony
 * All rights reserved.
 */
package com.nhncorp.usf.core.interceptor.impl.multipart;

import java.io.File;
import java.io.IOException;
import java.util.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.opensymphony.xwork.ActionContext;
import com.opensymphony.xwork.util.OgnlValueStack;

/**
 * Parses a multipart request and provides a wrapper around the request.
 * Simplify webwork multipart request. copy lucy-web's JakartaMultiPartRequest.
 * 
 */
public class MultiPartRequestWrapper extends HttpServletRequestWrapper {
    protected static final Log LOG = LogFactory.getLog(MultiPartRequestWrapper.class);

    @SuppressWarnings("unchecked")
	Collection errors;
    MultiPartRequest multi;

    /**
     * Instantiates the appropriate MultiPartRequest parser implementation and processes the data.
     *
     * @param request the servlet request object
     * @param saveDir directory to save the file(s) to
     * @param maxSize maximum file size allowed
     */
    @SuppressWarnings("unchecked")
	public MultiPartRequestWrapper(HttpServletRequest request, String saveDir, int maxSize) {
        super(request);

        if (request instanceof MultiPartRequest) {
            multi = (MultiPartRequest)request;
        } else {
            try {
                // instantiate it
                multi = new JakartaMultiPartRequest(request, saveDir, maxSize);

                for (Iterator iter = multi.getErrors().iterator(); iter.hasNext();) {
                    String error = (String)iter.next();
                    addError(error);
                }

            } catch (IOException e) {
                LOG.error(e.toString(), e);
                addError("Access errror for JakartaMultiPartRequest: " + e);
            }
        }
    }

    /**
     * @deprecated use {@link #getFileParameterNames()} instead
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
	public Enumeration getFileNames() {
        return getFileParameterNames();
    }

    /**
     * Get an enumeration of the parameter names for uploaded files
     *
     * @return enumeration of parameter names for uploaded files
     */
    @SuppressWarnings("unchecked")
	public Enumeration getFileParameterNames() {
        if (multi == null) {
            return null;
        }

        return multi.getFileParameterNames();
    }

    /**
     * @deprecated use {@link #getContentTypes(String)} instead
     * {@inheritDoc}
     */
    public String getContentType(String fieldName) {
        String[] contentTypes = getContentTypes(fieldName);

        if (contentTypes != null && contentTypes.length > 0) {
            return contentTypes[0];
        }

        return null;
    }

    /**
     * Get an array of content encoding for the specified input field name or <tt>null</tt> if
     * no content type was specified.
     *
     * @param name input field name
     * @return an array of content encoding for the specified input field name
     */
    public String[] getContentTypes(String name) {
        if (multi == null) {
            return null;
        }

        return multi.getContentType(name);
    }

    /**
     * @deprecated use {@link #getFiles(String)} instead
     * {@inheritDoc}
     */
    public File getFile(String fieldName) {
        File[] files = getFiles(fieldName);

        if (files != null && files.length > 0) {
            return files[0];
        }

        return null;
    }

    /**
     * Get a {@link java.io.File[]} for the given input field name.
     *
     * @param fieldName input field name
     * @return a File[] object for files associated with the specified input field name
     */
    public File[] getFiles(String fieldName) {
        if (multi == null) {
            return null;
        }

        return multi.getFile(fieldName);
    }

    /**
     * Get a String array of the file names for uploaded files
     * 
     * @param fieldName the field name
     * @return a String[] of file names for uploaded files
     */
    public String[] getFileNames(String fieldName) {
        if (multi == null) {
            return null;
        }

        return multi.getFileNames(fieldName);
    }

    /**
     * @deprecated use {@link #getFileSystemNames(String)} instead
     * {@inheritDoc}
     */
    public String getFilesystemName(String fieldName) {
        String[] names = getFileSystemNames(fieldName);

        if (names != null && names.length > 0) {
            return names[0];
        }

        return null;
    }

    /**
     * Get the filename(s) of the file(s) uploaded for the given input field name.
     * Returns <tt>null</tt> if the file is not found.
     *
     * @param fieldName input field name
     * @return the filename(s) of the file(s) uploaded for the given input field name or
     *         <tt>null</tt> if name not found.
     */
    public String[] getFileSystemNames(String fieldName) {
        if (multi == null) {
            return null;
        }

        return multi.getFilesystemName(fieldName);
    }

    /**
     * @see javax.servlet.http.HttpServletRequest#getParameter(String)
     * {@inheritDoc}
     */
    public String getParameter(String name) {
        return ((multi == null) || (multi.getParameter(name) == null)) ? super.getParameter(name)
                : multi.getParameter(name);
    }

    /**
     * @see javax.servlet.http.HttpServletRequest#getParameterMap()
     *      list가 아닌 경우 일반 object를 가져오도록 수정.
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
	public Map getParameterMap() {
        Map map = new HashMap();
        Enumeration enumeration = getParameterNames();

        while (enumeration.hasMoreElements()) {
            String name = (String)enumeration.nextElement();
            Object object = this.getParameterValues(name);

            if (object == null) {
                continue;
            }

            if (object instanceof String[]) {
                String[] values = (String[])object;

                if (values.length == 1) {
                    map.put(name, values[0]);
                } else {
                    map.put(name, values);
                }

            } else {
                map.put(name, object);
            }
        }

        return map;
    }

    /**
     * @see javax.servlet.http.HttpServletRequest#getParameterNames()
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
	public Enumeration getParameterNames() {
        if (multi == null) {
            return super.getParameterNames();
        } else {
            return mergeParams(multi.getParameterNames(), super.getParameterNames());
        }
    }

    /**
     * @see javax.servlet.http.HttpServletRequest#getParameterValues(String)
     * {@inheritDoc}
     */
    public String[] getParameterValues(String name) {
        return ((multi == null) || (multi.getParameterValues(name) == null)) ? super.getParameterValues(name)
                : multi.getParameterValues(name);
    }

    /**
     * Returns <tt>true</tt> if any errors occured when parsing the HTTP multipart request, <tt>false</tt> otherwise.
     *
     * @return <tt>true</tt> if any errors occured when parsing the HTTP multipart request, <tt>false</tt> otherwise.
     */
    public boolean hasErrors() {
        if ((errors == null) || errors.isEmpty()) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * Returns a collection of any errors generated when parsing the multipart request.
     *
     * @return the error Collection.
     */
    @SuppressWarnings("unchecked")
	public Collection getErrors() {
        return errors;
    }

    /**
     * Adds an error message.
     *
     * @param anErrorMessage the error message to report.
     */
    @SuppressWarnings("unchecked")
	protected void addError(String anErrorMessage) {
        if (errors == null) {
            errors = new ArrayList();
        }

        errors.add(anErrorMessage);
    }

    /**
     * Merges 2 enumeration of parameters as one.
     *
     * @param params1 the first enumeration.
     * @param params2 the second enumeration.
     * @return a single Enumeration of all elements from both Enumerations.
     */
    @SuppressWarnings("unchecked")
	protected Enumeration mergeParams(Enumeration params1, Enumeration params2) {
        Vector temp = new Vector();

        while (params1.hasMoreElements()) {
            temp.add(params1.nextElement());
        }

        while (params2.hasMoreElements()) {
            temp.add(params2.nextElement());
        }

        return temp.elements();
    }

    /**
     * @see javax.servlet.ServletRequestWrapper#getAttribute(java.lang.String)
     * {@inheritDoc}
     */
    public Object getAttribute(String key) {
        if (key != null && key.startsWith("javax.servlet")) {
            // don't bother with the standard javax.servlet attributes, we can short-circuit this
            // see WW-953 and the forums post linked in that issue for more info
            return super.getAttribute(key);
        }

        Object attribute = super.getAttribute(key);

        boolean alreadyIn = false;

        // WW-1365
        ActionContext ctx = ActionContext.getContext();
        Boolean aBoolean = (Boolean)ctx.get("__requestWrapper.getAttribute");

        if (aBoolean != null) {
            alreadyIn = aBoolean.booleanValue();
        }

        // note: we don't let # come through or else a request for
        // #attr.foo or #request.foo could cause an endless loop
        if (!alreadyIn && attribute == null && (key != null && key.indexOf("#") == -1)) {
            try {
                // If not found, then try the ValueStack
                ctx.put("__requestWrapper.getAttribute", Boolean.TRUE);
                OgnlValueStack stack = ctx.getValueStack();

                if (stack != null) {
                    attribute = stack.findValue(key);
                }

            } finally {
                ctx.put("__requestWrapper.getAttribute", Boolean.FALSE);
            }
        }

        return attribute;
    }
}
